import { App } from 'vue'

const defaultCfg = {
    threshold: 0,
    root: null,
    rootMargin: '0px'
}

function update(el: any, ctx: any, value: any) {
    let handler, cfg, changed

    if (typeof value === 'function') {
        handler = value
        cfg = defaultCfg
    } else {
        handler = value.handler
        cfg = Object.assign({}, defaultCfg, value.cfg)
    }
    changed = ctx.cfg === void 0

    if (ctx.handler !== handler) {
        ctx.handler = handler
    }

    if (changed === true) {
        ctx.cfg = cfg
        ctx.observer !== void 0 && ctx.observer.unobserve(el)

        ctx.observer = new IntersectionObserver(([entry]) => {
            if (typeof ctx.handler === 'function') {
                // if observed element is part of a vue transition
                // then we need to be careful...
                if (entry.rootBounds === null && document.body.contains(el) === true) {
                    ctx.observer.unobserve(el)
                    ctx.observer.observe(el)
                    return
                }

                const res = ctx.handler(entry, ctx.observer)

                if (res === false || (ctx.once === true && entry.isIntersecting === true)) {
                    destroy(el)
                }
            }
        }, cfg)

        ctx.observer.observe(el)
    }
}

function destroy(el: any) {
    const ctx = el.__qvisible

    if (ctx !== void 0) {
        ctx.observer !== void 0 && ctx.observer.unobserve(el)
        delete el.__qvisible
    }
}
export const intersection = (app: App<Element>) => {
    app.directive('intersection', {
        mounted(el, { modifiers, value }) {
            const ctx = {
                once: modifiers.once === true
            }

            update(el, ctx, value)

            el.__qvisible = ctx
        },

        updated(el, binding) {
            const ctx = el.__qvisible
            ctx !== void 0 && update(el, ctx, binding.value)
        },

        beforeUnmount: destroy
    })
}
